﻿
Imports std

Public Module randomfiletp

    Public Class arguments
        Public Const port As String = "port"
        Public Const allowdir As String = "allowdir"
        Public Const password As String = "password"
    End Class

    Public Class query
        Public Const file As String = "f"
        Public Const password As String = "p"
    End Class

    Public WithEvents server As httpServer2 = Nothing
    Public allowdirs As vector(Of String) = Nothing
    Public password As String = Nothing
    Public passwordpage As passwordpage = Nothing

    Friend pathnormalizer As vector(Of pair(Of String, String)) = Nothing

    Public Sub unhandledException(ByVal ex As Exception)
        server.stop()
        forceQuit(0)
    End Sub

    Friend unhandledExceptionHandler As UnhandledExceptionDelegate = AddressOf unhandledException

    Public Sub main(ByVal iargs() As String)
        registerConsoleExtHandle()
        errorHandle.initial(defaultLogFilename())
        If Not isdebugmode() Then
            registerUnhandledException(unhandledExceptionHandler)
        End If
        destructor.start()

        Dim args As map(Of String, vector(Of String)) = Nothing
        argmgr.parse(iargs, args, True)

        server = New httpServer2()
        If argmgr.containsValue(args, arguments.port, True) Then
            Dim ports As vector(Of String) = Nothing
            ports = args(arguments.port)
            Dim i As Int64
            For i = 0 To ports.size() - 1
                raiseError("add ports " + ports(i))
                server.addports(ports(i))
            Next
        Else
            raiseError("add port 80 <default>")
            server.addport(80)
        End If

        allowdirs = New vector(Of String)
        If argmgr.containsValue(args, arguments.allowdir, True) Then
            allowdirs.push_back(args(arguments.allowdir))
        End If
        If Not argmgr.getfirstvalue(args, arguments.password, password) Then
            password = emptyString
        End If

        pathnormalizer = New vector(Of pair(Of String, String))
        pathnormalizer.push_back(make_pair(Of String, String)(character.leftSlash, character.rightSlash))
        passwordpage = New passwordpage()

        raiseError("httpServer2 start.")
        server.start()

        While Not consoleExtSignal() AndAlso server.health()
            sleep(16000)
        End While

        If consoleExtSignal() Then
            raiseError("caught console cancel key event, shutdown.")
        Else
            raiseError("httpServer2 failed, shutdown.", errorHandle.errorType.exclamation)
        End If

        server.stop()

        destructor.stop()
        errorHandle.finalize()
        consoleExtHandleFinish()
    End Sub

    Public Function isAllowdir(ByRef path As String) As Boolean
        assert(Not allowdirs Is Nothing, "allowdirs is nothing before calling isAllowdir(path).")

        Dim i As Int64
        For i = 0 To allowdirs.size() - 1
            If strsame(allowdirs(i), path, strlen(allowdirs(i)), False) Then
                Return True
            End If
        Next

        Return False
    End Function

    Public Function getfile(ByRef s As String, ByRef buff As bytes) As Boolean
        On Error Resume Next
        If IO.File.Exists(s) Then
            buff = New bytes(IO.File.ReadAllBytes(s))
        End If

        Return Not buff Is Nothing
    End Function

    Public Sub processError(ByRef context As httpContext)
        context.setstatuscode(Net.HttpStatusCode.NotFound, "NOTFOUND")
    End Sub

    Public Sub writeContext(ByRef context As httpContext, ByRef buff As bytes, ByRef filename As String)
        If context.setstatuscode(Net.HttpStatusCode.OK, "OK") Then
            context.buff.Write(buff.data, 0, buff.size())
            context.response.ContentEncoding = encoding.utf8
            context.response.ContentType = getdefaultContentType(filename)
        End If
    End Sub

    Public Function processInternalRequest(ByRef context As httpContext) As Boolean
        Dim path As String = Nothing
        path = context.url.path
        If strsame(path, "/ver", False) Then
            If context.setstatuscode(Net.HttpStatusCode.OK, "OK") Then
                context.writer.WriteLine(applicationSign())
                context.response.ContentEncoding = encoding.utf8
                context.response.ContentType = network.http.contentType.texthtmlUtf8
            End If

            Return True
        Else
            Return False
        End If
    End Function

    Private Sub server_receive(ByVal context As httpContext) Handles server.receive
        assert(Not context Is Nothing, "context is nothing when server_receive.")

        If processInternalRequest(context) Then
            Return
        End If

        Dim key As String = Nothing
        key = context.url.query(query.file)
        kickLeftRightBlank(key)
        If isemptystring(key) Then
            raiseError("get request with empty query, ignore.", errorHandle.errorType.warning)
            processError(context)
            Return
        End If
        strrplc2(key, pathnormalizer, False)

        If isdebugmode() Then
            raiseError("get request from " + context.request.RemoteEndPoint.ToString() + " with query " + key _
                       , errorHandle.errorType.application)
        End If
        Dim pwd As String = Nothing
        pwd = context.url.query(query.password)

        If isemptystring(password) OrElse strsame(pwd, password, True) Then
            Dim path As String = Nothing
            Dim file As String = Nothing
            file = getfilename(key)
            path = parentpath(key)
            Dim buff As bytes = Nothing

            If isAllowdir(path) AndAlso getfile(key, buff) Then
                writeContext(context, buff, key)
                Return
            End If
        End If

        passwordpage.process(context, key)
    End Sub
End Module
